/*************************************************************************/
/*                                                                       */
/*  Copyright (c) 1994 Stanford University                               */
/*                                                                       */
/*  All rights reserved.                                                 */
/*                                                                       */
/*  Permission is given to use, copy, and modify this software for any   */
/*  non-commercial purpose as long as this copyright notice is not       */
/*  removed.  All other uses, including redistribution in whole or in    */
/*  part, are forbidden without prior written permission.                */
/*                                                                       */
/*  This software is provided with absolutely no warranty and no         */
/*  support.                                                             */
/*                                                                       */
/*************************************************************************/

/*
 *	Element management.
 *
 *       This module has the following functionalities.
 *       (1) Creation/initialization of a new instance of an element object.
 *       (2) Recursive refinement of elements.
 */


#include <stdio.h>

EXTERN_ENV;

include(radiosity.h)

static void _foreach_element(Element *elem, void (*func)(), long arg1, long process_id);
static void _foreach_leaf_element(Element *elem, void (*func)(), long arg1, long process_id);
static long bf_refine_element(long subdiv, Element *elem, Interaction *inter, long process_id);
static void process_rays2(Element *e, long process_id);
static void process_rays3(Element *e, long process_id);
static void elem_join_operation(Element *e, Element *ec, long process_id);
static void gather_rays(Element *elem, long process_id);
static float _diff_disc_formfactor(Vertex *p, Element *e_src, Vertex *p_disc, float area, Vertex *normal, long process_id);
static float compute_diff_disc_formfactor(Vertex *p, Element *e_src, Vertex *p_disc, Element *e_dst, long process_id);

/***************************************************************************
 ****************************************************************************
 *
 *    Methods for Element object
 *
 ****************************************************************************
 ****************************************************************************
 ****************************************************************************
 *
 *    foreach_element_in_patch()
 *    foreach_leaf_element_in_patch()
 *
 *    General purpose driver. For each element of a patch, apply specified
 *    function.  The function is passed a pointer to the element.
 *    Traversal is DFS (post-order).
 *
 ****************************************************************************/

void foreach_element_in_patch(Patch *patch, void (*func)(), long  arg1, long process_id)
{
    _foreach_element( patch->el_root, func, arg1, process_id ) ;
}


static void _foreach_element(Element *elem, void (*func)(), long   arg1, long process_id)
{
    if( elem == 0 )
        return ;

    /* Process children */
    if( ! LEAF_ELEMENT( elem ) )
        {
            _foreach_element( elem->center, func, arg1, process_id ) ;
            _foreach_element( elem->top,    func, arg1, process_id ) ;
            _foreach_element( elem->left,   func, arg1, process_id ) ;
            _foreach_element( elem->right,  func, arg1, process_id ) ;
        }

    /* Apply function to this node */
    func( elem, arg1, process_id ) ;
}


void foreach_leaf_element_in_patch(Patch *patch, void (*func)(), long  arg1, long process_id)
{
    _foreach_leaf_element( patch->el_root, func, arg1, process_id ) ;
}


static void _foreach_leaf_element(Element *elem, void (*func)(), long arg1, long process_id )
{
    if( elem == 0 )
        return ;

    /* Process children */
    if( LEAF_ELEMENT( elem ) )
        func( elem, arg1, process_id ) ;
    else
        {
            _foreach_leaf_element( elem->center, func, arg1, process_id ) ;
            _foreach_leaf_element( elem->top,    func, arg1, process_id ) ;
            _foreach_leaf_element( elem->left,   func, arg1, process_id ) ;
            _foreach_leaf_element( elem->right,  func, arg1, process_id ) ;
        }
}



/***************************************************************************
 *
 *    ff_refine_elements()
 *
 *    Recursively refine the elements based on FF estimate.
 *
 ****************************************************************************/

void ff_refine_elements(Element *e1, Element *e2, long level, long process_id)
{
    long subdiv_advice ;
    Interaction i12, i21 ;
    Interaction *inter ;

#if PATCH_ASSIGNMENT == PATCH_ASSIGNMENT_COSTBASED
#if defined(SUN4)
    Patch_Cost *pc1, *pc2 ;
#else
    volatile Patch_Cost *pc1, *pc2 ;
#endif
    long cost1, cost2 ;
#endif


    /* Now compute formfactor.
       As the BSP tree is being modified at this moment, don't test
       visibility. */
    compute_formfactor( e1, e2, &i12, process_id ) ;
    compute_formfactor( e2, e1, &i21, process_id ) ;

    /* Analyze the error of FF */
    subdiv_advice = error_analysis( e1, e2, &i12, &i21, process_id ) ;

    /* Execute subdivision procedure */
    if( NO_INTERACTION(subdiv_advice) )
        /* Two elements are mutually invisible. Do nothing */
        return ;

    else if( NO_REFINEMENT_NECESSARY(subdiv_advice) )
        {
            /* Create links and finish the job */
            inter = get_interaction(process_id) ;
            *inter = i12 ;
            inter->visibility = VISIBILITY_UNDEF ;
            insert_vis_undef_interaction( e1, inter, process_id ) ;

            inter = get_interaction(process_id) ;
            *inter = i21 ;
            inter->visibility = VISIBILITY_UNDEF ;
            insert_vis_undef_interaction( e2, inter, process_id ) ;

#if PATCH_ASSIGNMENT == PATCH_ASSIGNMENT_COSTBASED
            /* Update cost variable */
            pc1 = &global->patch_cost[ e1->patch->seq_no ] ;
            pc2 = &global->patch_cost[ e2->patch->seq_no ] ;
            if( pc1->n_total_inter <= 13 )
                cost1 = (long)ceil(e1->area / Area_epsilon) ;
            else
                cost1 = 1 ;

            if( pc2->n_total_inter <= 13 )
                cost2 = (long)ceil(e2->area / Area_epsilon) ;
            else
                cost2 = 1 ;

            LOCK(global->cost_sum_lock);
            pc1->cost_estimate += cost1 ;
            pc1->n_total_inter++ ;
            pc2->cost_estimate += cost2 ;
            pc2->n_total_inter++ ;
            global->cost_estimate_sum += (cost1 + cost2) ;
            global->cost_sum += (cost1 + cost2) ;
            UNLOCK(global->cost_sum_lock);
#endif
        }

    else if( REFINE_PATCH_1(subdiv_advice) )
        {
            /* Refine patch 1 */
            subdivide_element( e1, process_id ) ;

            /* Locally solve it */
            ff_refine_elements( e1->top,    e2, level+1, process_id ) ;
            ff_refine_elements( e1->center, e2, level+1, process_id ) ;
            ff_refine_elements( e1->left,   e2, level+1, process_id ) ;
            ff_refine_elements( e1->right,  e2, level+1, process_id ) ;
        }
    else
        {
            /* Refine patch 2 */
            subdivide_element( e2, process_id ) ;

            /* Locally solve it */
            ff_refine_elements( e1, e2->top,    level+1, process_id ) ;
            ff_refine_elements( e1, e2->center, level+1, process_id ) ;
            ff_refine_elements( e1, e2->left,   level+1, process_id ) ;
            ff_refine_elements( e1, e2->right,  level+1, process_id ) ;
        }
}



/***************************************************************************
 *
 *    error_analysis()
 *
 *    Analyze FF error.
 *
 ****************************************************************************/

long error_analysis(Element *e1, Element *e2, Interaction *inter12, Interaction *inter21, long process_id)
{
    long cc ;

    /* Check visibility */
    cc = patch_intersection( &e1->patch->plane_equ,
                            &e2->ev1->p, &e2->ev2->p, &e2->ev3->p, process_id ) ;
    if( NEGATIVE_SIDE(cc) )
        /* If negative or on the plane, then do nothing */
        return( _NO_INTERACTION ) ;

    cc = patch_intersection( &e2->patch->plane_equ,
                            &e1->ev1->p, &e1->ev2->p, &e1->ev3->p, process_id ) ;
    if( NEGATIVE_SIDE(cc) )
        /* If negative or on the plane, then do nothing */
        return( _NO_INTERACTION ) ;

    return( _NO_REFINEMENT_NECESSARY ) ;
}





/***************************************************************************
 *
 *    bf_refine_element()
 *
 *    Recursively refine the elements based on BF estimate.
 *    Return value: number of interactions newly created.
 *
 ****************************************************************************/

static long  bf_refine_element(long subdiv, Element *elem, Interaction *inter, long process_id)
{
    Element *e_dst = inter->destination ;
    Interaction *pi ;
    float visibility_val ;
    long new_inter = 0 ;


    visibility_val = NO_VISIBILITY_NECESSARY(subdiv)?
        (float)1.0 : VISIBILITY_UNDEF ;

    if( REFINE_PATCH_1(subdiv) )
        {
            /* Refine this element */
            /* (1) Make sure it has children */
            subdivide_element( elem, process_id ) ;

            /* (2) For each of the patch, create an interaction */
            if( element_completely_invisible( elem->center, e_dst, process_id ) == 0 )
                {
                    pi = get_interaction(process_id) ;
                    compute_formfactor( elem->center, e_dst, pi, process_id ) ;
                    pi->visibility = visibility_val ;
                    insert_vis_undef_interaction( elem->center, pi, process_id ) ;
                    new_inter++ ;
                }
            if( element_completely_invisible( elem->top, e_dst, process_id ) == 0 )
                {
                    pi = get_interaction(process_id) ;
                    compute_formfactor( elem->top, e_dst, pi, process_id ) ;
                    pi->visibility = visibility_val ;
                    insert_vis_undef_interaction( elem->top, pi, process_id ) ;
                    new_inter++ ;
                }
            if( element_completely_invisible( elem->left, e_dst, process_id ) == 0 )
                {
                    pi = get_interaction(process_id) ;
                    compute_formfactor( elem->left, e_dst, pi, process_id ) ;
                    pi->visibility = visibility_val ;
                    insert_vis_undef_interaction( elem->left, pi, process_id ) ;
                    new_inter++ ;
                }
            if( element_completely_invisible( elem->right, e_dst, process_id ) == 0 )
                {
                    pi = get_interaction(process_id) ;
                    compute_formfactor( elem->right, e_dst, pi, process_id ) ;
                    pi->visibility = visibility_val ;
                    insert_vis_undef_interaction( elem->right, pi, process_id ) ;
                    new_inter++ ;
                }
        }
    else
        {
            /* Refine source element */
            /* (1) Make sure it has children */
            subdivide_element( e_dst, process_id ) ;

            /* (2) Insert four new interactions
               NOTE: Use *inter as a place holder to link 4 new interactions
               since *prev may be NULL */

            if( element_completely_invisible( elem, e_dst->center, process_id ) == 0 )
                {
                    pi = get_interaction(process_id) ;
                    compute_formfactor( elem, e_dst->center, pi, process_id ) ;
                    pi->visibility = visibility_val ;
                    insert_vis_undef_interaction( elem, pi, process_id ) ;
                    new_inter++ ;
                }
            if( element_completely_invisible( elem, e_dst->top, process_id ) == 0 )
                {
                    pi = get_interaction(process_id) ;
                    compute_formfactor( elem, e_dst->top, pi, process_id ) ;
                    pi->visibility = visibility_val ;
                    insert_vis_undef_interaction( elem, pi, process_id ) ;
                    new_inter++ ;
                }
            if( element_completely_invisible( elem, e_dst->left, process_id ) == 0 )
                {
                    pi = get_interaction(process_id) ;
                    compute_formfactor( elem, e_dst->left, pi, process_id ) ;
                    pi->visibility = visibility_val ;
                    insert_vis_undef_interaction( elem, pi, process_id ) ;
                    new_inter++ ;
                }
            if( element_completely_invisible( elem, e_dst->right, process_id ) == 0 )
                {
                    pi = get_interaction(process_id) ;
                    compute_formfactor( elem, e_dst->right, pi, process_id ) ;
                    pi->visibility = visibility_val ;
                    insert_vis_undef_interaction( elem, pi, process_id ) ;
                    new_inter++ ;
                }
        }

    return( new_inter ) ;
}




/***************************************************************************
 *
 *    bf_error_analysis_list()
 *
 *    For each interaction in the list, analyze error of BF value.
 *    If error is lower than the limit, the interaction is linked to the
 *    normal interaction list. Otherwise, BF-refinement is performed and
 *    the newly created interactions are linked to the vis-undef-list.
 *
 ****************************************************************************/

void bf_error_analysis_list(Element *elem, Interaction *i_list, long process_id)
{
    long subdiv_advice ;
    Interaction *prev = 0 ;
    Interaction *inter = i_list ;
    Interaction *refine_inter ;
    long i_len = 0 ;
    long delta_n_inter = 0 ;


    while( inter )
        {
            /* Analyze error */
            subdiv_advice = bf_error_analysis( elem, inter, process_id ) ;

            if( NO_REFINEMENT_NECESSARY(subdiv_advice) )
                {
                    /* Go on to the next interaction */
                    prev = inter ;
                    inter = inter->next ;
                    i_len++ ;
                }
            else
                {
                    /* Remove this interaction from the list */
                    refine_inter = inter ;
                    inter = inter->next ;
                    if( prev == 0 )
                        i_list = inter ;
                    else
                        prev->next = inter ;

                    /* Perform refine */
                    delta_n_inter += bf_refine_element( subdiv_advice,
                                                       elem, refine_inter, process_id ) ;

                    /* Delete this inter anyway */
                    free_interaction( refine_inter, process_id ) ;
                    delta_n_inter-- ;
                }
        }

    /* Link good interactions to elem->intearctions */
    if( i_len > 0 )
        {
            LOCK(elem->elem_lock->lock);
            prev->next = elem->interactions ;
            elem->interactions = i_list ;
            elem->n_interactions += i_len ;
            UNLOCK(elem->elem_lock->lock);
        }

#if PATCH_ASSIGNMENT == PATCH_ASSIGNMENT_COSTBASED
    /* Update patch interaction count */
    if( delta_n_inter != 0 )
        {
            Patch_Cost *pc ;

            pc = &global->patch_cost[ elem->patch->seq_no ] ;
            LOCK(pc->cost_lock->lock);
            pc->n_total_inter += delta_n_inter ;
            UNLOCK(pc->cost_lock->lock);
        }
#endif
}



/***************************************************************************
 *
 *    bf_error_analysis()
 *
 *    Analyze error of BF value.
 *    Return value: subdivision advice code.
 *
 ****************************************************************************/

long bf_error_analysis(Element *elem, Interaction *inter, long process_id)
{
    float rad_avg ;		     /* Average radiosity */
    float total_error ;
    float visibility_error ;
    long   vis_code = 0 ;

    /* Compute amount of error associated with the BF.

       FFcomputed = (F + Ferr)(V + Verr) = FV + (Ferr V + F Verr + Ferr Verr)
       BFcomputed = BF + BFerr
       = B FV + B (Ferr V + F Verr + Ferr Verr)
       */

    if( (0.0 < inter->visibility) && (inter->visibility < 1.0) )
        visibility_error = 1.0 ;
    else
        visibility_error = FF_VISIBILITY_ERROR ;

    rad_avg =( inter->destination->rad.r
              + inter->destination->rad.g
              + inter->destination->rad.b ) * (float)(1.0 / 3.0) ;

    total_error = (inter->visibility * inter->formfactor_err
                   + visibility_error * inter->formfactor_out
                   + visibility_error * inter->formfactor_err) * rad_avg ;

    /* If BF is smaller than the threshold, then not subdivide */
    if( (total_error <= BFepsilon) && (elem->n_interactions <= 10) )
        return( _NO_REFINEMENT_NECESSARY ) ;
    else if( total_error <= BFepsilon * 0.5 )
        return( _NO_REFINEMENT_NECESSARY ) ;

    /* Subdivide light source or receiver whichever is larger */
    if( elem->area > inter->destination->area )
        {
            if( elem->area > Area_epsilon )
                /* Subdivide this element (receiver) */
                return( _REFINE_PATCH_1 | vis_code ) ;
        }
    else
        {
            if( inter->destination->area > Area_epsilon )

                /* Subdivide source element */
                return( _REFINE_PATCH_2 | vis_code ) ;
        }

    return( _NO_REFINEMENT_NECESSARY ) ;
}





/***************************************************************************
 *
 *    radiosity_converged()
 *
 *    Return TRUE(1) when the average radiosity value is converged.
 *
 ****************************************************************************/

long radiosity_converged(long process_id)
{
    float prev_total, current_total ;
    float difference ;
    Rgb rad ;

    /* Check radiosity value */
    prev_total = global->prev_total_energy.r + global->prev_total_energy.g
        + global->prev_total_energy.b ;
    current_total = global->total_energy.r + global->total_energy.g
        + global->total_energy.b ;

    /* Compute difference from the previous iteration */
    prev_total += 1.0e-4 ;
    difference = fabs( (current_total / prev_total) - (float)1.0 ) ;

    if( verbose_mode )
        {
            rad = global->total_energy ;
            rad.r /= global->total_patch_area ;
            rad.g /= global->total_patch_area ;
            rad.b /= global->total_patch_area ;
            printf( "Total energy:     " ) ;
            print_rgb( &global->total_energy ) ;
            printf( "Average radiosity:" ) ;
            print_rgb( &rad ) ;
            printf( "Difference %.2f%%\n", difference * 100.0 ) ;
        }

    if( difference <=  Energy_epsilon )
        return( 1 ) ;
    else
        return( 0 ) ;
}


/***************************************************************************
 *
 *    subdivide_element()
 *
 *    Create child elements. If they already exist, do nothing.
 *
 ****************************************************************************/

void subdivide_element(Element *e, long process_id)
{
    float quarter_area ;
    ElemVertex *ev_12, *ev_23, *ev_31 ;
    Edge *e_12_23, *e_23_31, *e_31_12 ;
    Element *enew, *ecenter ;
    long rev_12, rev_23, rev_31 ;

    /* Lock the element before checking the value */
    LOCK(e->elem_lock->lock);

    /* Check if the element already has children */
    if( ! _LEAF_ELEMENT(e) )
        {
            UNLOCK(e->elem_lock->lock);
            return ;
        }

    /* Subdivide edge structures */
    subdivide_edge( e->e12, (float)0.5, process_id ) ;
    subdivide_edge( e->e23, (float)0.5, process_id ) ;
    subdivide_edge( e->e31, (float)0.5, process_id ) ;
    ev_12 = e->e12->ea->pb ;
    ev_23 = e->e23->ea->pb ;
    ev_31 = e->e31->ea->pb ;

    /* Then create new edges */
    e_12_23 = create_edge( ev_12, ev_23, process_id ) ;
    e_23_31 = create_edge( ev_23, ev_31, process_id ) ;
    e_31_12 = create_edge( ev_31, ev_12, process_id ) ;

    /* Area parameters */
    quarter_area = e->area * (float)0.25 ;

    /* (1) Create the center patch */
    enew = get_element(process_id) ;
    ecenter = enew ;
    enew->parent= e ;
    enew->patch = e->patch ;
    enew->ev1   = ev_23 ;
    enew->ev2   = ev_31 ;
    enew->ev3   = ev_12 ;
    enew->e12   = e_23_31 ;
    enew->e23   = e_31_12 ;
    enew->e31   = e_12_23 ;
    enew->area  = quarter_area ;
    enew->rad   = e->rad ;

    /* (2) Create the top patch */
    rev_12 = EDGE_REVERSE( e->e12, e->ev1, e->ev2 ) ;
    rev_23 = EDGE_REVERSE( e->e23, e->ev2, e->ev3 ) ;
    rev_31 = EDGE_REVERSE( e->e31, e->ev3, e->ev1 ) ;

    enew = get_element(process_id) ;
    e->top = enew ;
    enew->parent= e ;
    enew->patch = e->patch ;
    enew->ev1   = e->ev1 ;
    enew->ev2   = ev_12 ;
    enew->ev3   = ev_31 ;
    enew->e12   = (!rev_12)? e->e12->ea : e->e12->eb ;
    enew->e23   = e_31_12 ;
    enew->e31   = (!rev_31)? e->e31->eb : e->e31->ea ;
    enew->area  = quarter_area ;
    enew->rad   = e->rad ;

    /* (3) Create the left patch */
    enew = get_element(process_id) ;
    e->left = enew ;
    enew->parent= e ;
    enew->patch = e->patch ;
    enew->ev1   = ev_12 ;
    enew->ev2   = e->ev2 ;
    enew->ev3   = ev_23 ;
    enew->e12   = (!rev_12)? e->e12->eb : e->e12->ea ;
    enew->e23   = (!rev_23)? e->e23->ea : e->e23->eb ;
    enew->e31   = e_12_23 ;
    enew->area  = quarter_area ;
    enew->rad   = e->rad ;

    /* (4) Create the right patch */
    enew = get_element(process_id) ;
    e->right = enew ;
    enew->parent= e ;
    enew->patch = e->patch ;
    enew->ev1   = ev_31 ;
    enew->ev2   = ev_23 ;
    enew->ev3   = e->ev3 ;
    enew->e12   = e_23_31 ;
    enew->e23   = (!rev_23)? e->e23->eb : e->e23->ea ;
    enew->e31   = (!rev_31)? e->e31->ea : e->e31->eb ;
    enew->area  = quarter_area ;
    enew->rad   = e->rad ;

    /* Finally, set e->center */
    e->center = ecenter ;

    /* Unlock the element */
    UNLOCK(e->elem_lock->lock);
}


/***************************************************************************
 *
 *    process_rays()
 *
 *    Gather rays.
 *
 *    Solution strategy:
 *            Original radiosity equation:    [I-M] B = E
 *            Iterative solution:                           2    3
 *                                            B = [I + M + M  + M  ...] E
 *            Sollution by this routine:      Bn = E + M Bn-1
 *                                            (B0 = E)
 *
 *
 *    process_rays() first checks the amount of error associated with each
 *    interaction. If it is large, then the interaction is refined.
 *    After all the interactions are examined, the light energy is
 *    gathered. This gathered energy and the energy passed from the ancestors
 *    are added and pushed down to descendants. The descendants in turn pop up
 *    the area weighted energy gathered at descendant level. Thus, the
 *    radiosity at level i is:
 *          Bi =  E
 *              + Sum(j=0 to i-1) gather(j)        --- gathered at ancestors
 *              + gather(i)                        --- gathered at this level
 *              + 1/4( Bi+1.right + Bi+1.left + Bi+1.top + Bi+1.center)
 *                                                 --- gathered at descendants
 *
 *    The implementation is slightly complicated due to many fork and join
 *    operations that occur when:
 *      (1) four children are visited and the results are added to Bi.
 *      (2) parallel visibility computation is done and then process_rays() resumed.
 *
 *    The former case would be a simple (non-tail)-recursive code in a
 *    uniprocessor program. However, because we create tasks to process lower
 *    levels and the creation of tasks does not mean the completion of
 *    processing at the lower level, we use a mechanism to suspend
 *    processing at this level and later resume it after the child tasks are
 *    finished.
 *    To be portable across many systems, in this implementation procedures
 *    are split at the point of task creation so that the subtask can call
 *    the next portion of the procedure on exit of the task.
 *    This mechanism can be best explained as the continuation-passing-style
 *    (CPS) where continuation means a lambda expression which represents 'the
 *    rest of the program'.
 *
 ****************************************************************************/


void process_rays(Element *e, long process_id)
{
    Interaction *i_list ;

    /* Detach interactions from the list */
    LOCK(e->elem_lock->lock);
    i_list = e->interactions ;
    e->interactions = (Interaction *)0 ;
    e->n_interactions = 0 ;
    UNLOCK(e->elem_lock->lock);

    /* For each interaction, do BF-error-analysis */
    bf_error_analysis_list( e, i_list, process_id ) ;

    if( e->n_vis_undef_inter == 0 )
        process_rays3( e, process_id ) ;
    else
        /* Some interactions were refined.
           Compute visibility and do BF-analysis again */
        create_visibility_tasks( e, process_rays2, process_id ) ;
}




static void process_rays2(Element *e, long process_id)
{
    Interaction *i_list ;

    /* Detach interactions from the vis-undef-list. They now have their
       visibility computed */
    LOCK(e->elem_lock->lock);
    i_list = e->vis_undef_inter ;
    e->vis_undef_inter = (Interaction *)0 ;
    e->n_vis_undef_inter = 0 ;
    UNLOCK(e->elem_lock->lock);

    /* For each interaction, do BF-error-analysis */
    bf_error_analysis_list( e, i_list, process_id ) ;

    if( e->n_vis_undef_inter == 0 )
        process_rays3( e, process_id ) ;
    else
        /* Some interactions were refined.
           Compute visibility and do BF-analysis again */
        create_visibility_tasks( e, process_rays2, process_id ) ;
}



static void process_rays3(Element *e, long process_id)
{
    Rgb rad_push ;		          /* Radiosity value pushed down */


    /* Gather light rays that impinge on this element */
    gather_rays( e, process_id ) ;

    /* Now visit children */
    if( ! LEAF_ELEMENT(e) )
        {
            /* Compute radiosity that is pushed to descendents */
            rad_push.r = e->rad_in.r + e->rad_subtree.r ;
            rad_push.g = e->rad_in.g + e->rad_subtree.g ;
            rad_push.b = e->rad_in.b + e->rad_subtree.b ;

            e->center->rad_in = rad_push ;
            e->top->   rad_in = rad_push ;
            e->right-> rad_in = rad_push ;
            e->left->  rad_in = rad_push ;
            e->join_counter = 4 ;

            /* Create tasks to process children */
            create_ray_task( e->center, process_id ) ;
            create_ray_task( e->top, process_id ) ;
            create_ray_task( e->left, process_id ) ;
            create_ray_task( e->right, process_id ) ;

            /* The rest of the job (pop up the computed radiosity) is
               handled by the continuation function, elem_join_operation().
               It is called when the lower level finishes.
               On exit, elem_join_operation() calls the continuation of the
               parent, which is also elem_join_operation().
               To be general, those tasks should be passed the continuation
               (elem_join_operation). But, since the continuation is obvious
               in this context, it is hardwired below */
        }
    else
        {
            /* Update element radiosity at the leaf level */
            e->rad.r = e->rad_in.r + e->rad_subtree.r + e->patch->emittance.r ;
            e->rad.g = e->rad_in.g + e->rad_subtree.g + e->patch->emittance.g ;
            e->rad.b = e->rad_in.b + e->rad_subtree.b + e->patch->emittance.b ;

            /* Ship out radiosity to the parent */
            elem_join_operation( e->parent, e, process_id ) ;
        }
}



/***************************************************************************
 *
 *    elem_join_operation()
 *
 *    This function performs the second half of the function of process_rays.
 *    That is, i)   add area weighted child radiosity
 *             ii)  update radiosity variable
 *             iii) call continuation of the parent (ie, elem_join_operation
 *                  itself)
 *
 *    Thus, in spirit, the function is a tail recursive routine although
 *    tail-recursion-elimination is manually performed in the code below.
 *
 ****************************************************************************/



static void elem_join_operation(Element *e, Element *ec, long process_id)
{
    long join_flag ;
#if PATCH_ASSIGNMENT == PATCH_ASSIGNMENT_COSTBASED
    Patch_Cost *pc ;
#endif


    while( e != 0 )
        {
            /* Get radiosity of the child and add to my radiosity */
            LOCK(e->elem_lock->lock);
            e->rad_subtree.r += ec->rad_subtree.r * (float)0.25 ;
            e->rad_subtree.g += ec->rad_subtree.g * (float)0.25 ;
            e->rad_subtree.b += ec->rad_subtree.b * (float)0.25 ;
            e->join_counter-- ;
            join_flag = (e->join_counter == 0) ;
            UNLOCK(e->elem_lock->lock);

            if( join_flag == 0 )
                /* Other children are not finished. Return. */
                return ;

            /* This is the continuation called by the last (4th) subprocess.
               Perform JOIN at this level */
            /* Update element radiosity */
            e->rad.r = e->rad_in.r + e->rad_subtree.r + e->patch->emittance.r ;
            e->rad.g = e->rad_in.g + e->rad_subtree.g + e->patch->emittance.g ;
            e->rad.b = e->rad_in.b + e->rad_subtree.b + e->patch->emittance.b ;

            /* Traverse the tree one level up and repeat */
            ec = e ;
            e = e->parent ;
        }

    /* Process RAY root level finished. Update energy variable */
    LOCK(global->avg_radiosity_lock);
    global->total_energy.r += ec->rad.r * ec->area ;
    global->total_energy.g += ec->rad.g * ec->area ;
    global->total_energy.b += ec->rad.b * ec->area ;
    global->total_patch_area += ec->area ;
    UNLOCK(global->avg_radiosity_lock);

#if PATCH_ASSIGNMENT == PATCH_ASSIGNMENT_COSTBASED
    /* Then update the cost variable of the patch */
    pc = &global->patch_cost[ ec->patch->seq_no ] ;
    pc->cost_history[3] = pc->cost_history[2] ;
    pc->cost_history[2] = pc->cost_history[1] ;
    pc->cost_history[1] = pc->cost_history[0] ;
    pc->cost_history[0] = PATCH_COST( pc ) ;
    pc->cost_estimate   = PATCH_COST_ESTIMATE( pc ) ;

    /* Also, update the global cost variable */
    LOCK(global->cost_sum_lock);
    global->cost_sum          += pc->cost_history[0] ;
    global->cost_estimate_sum += pc->cost_estimate ;
    UNLOCK(global->cost_sum_lock);
#endif

}



/***************************************************************************
 *
 *    gather_rays()
 *
 ****************************************************************************/

static void gather_rays(Element *elem, long process_id)
{
    Rgb rad_elem ;		/* Radiosity gathered by this elem */
    float ff_v ;		/* Form factor times visibility */
    float bf_r, bf_g, bf_b ;
    Interaction *inter ;


    /* Return immediately if there is no interaction */
    if( (inter = elem->interactions) == 0 )
        {
            elem->rad_subtree.r = 0.0 ;
            elem->rad_subtree.g = 0.0 ;
            elem->rad_subtree.b = 0.0 ;
            return ;
        }


    /* Gather rays of this element
       (do it directly without the driver function, 'Foreach-interaction') */
    rad_elem.r = 0.0 ;
    rad_elem.g = 0.0 ;
    rad_elem.b = 0.0 ;

    while( inter )
        {
            /* Be careful !
               Use FF(out) to compute incoming energy */
            ff_v = inter->formfactor_out * inter->visibility ;
            bf_r = ff_v * inter->destination->rad.r ;
            bf_g = ff_v * inter->destination->rad.g ;
            bf_b = ff_v * inter->destination->rad.b ;

            rad_elem.r += bf_r ;
            rad_elem.g += bf_g ;
            rad_elem.b += bf_b ;

            /* Update pointers */
            inter = inter->next ;
        }

    /* Multiply the gathered radiosity by the diffuse color of this element */
    rad_elem.r *= elem->patch->color.r ;
    rad_elem.g *= elem->patch->color.g ;
    rad_elem.b *= elem->patch->color.b ;

    /* Store the value at the initial value of 'rad_subtree' */
    elem->rad_subtree = rad_elem ;
}




/***************************************************************************
 *
 *    elememnt_completely_invisible()
 *
 *    Fast primary visibility test.
 *
 ****************************************************************************/

long element_completely_invisible(Element *e1, Element *e2, long process_id)
{
    long cc ;

    /* Check visibility */
    cc = patch_intersection( &e1->patch->plane_equ,
                            &e2->ev1->p, &e2->ev2->p, &e2->ev3->p, process_id ) ;
    if( NEGATIVE_SIDE(cc) )
        /* If negative or on the plane, then do nothing */
        return( 1 ) ;

    cc = patch_intersection( &e2->patch->plane_equ,
                            &e1->ev1->p, &e1->ev2->p, &e1->ev3->p, process_id ) ;
    if( NEGATIVE_SIDE(cc) )
        /* If negative or on the plane, then do nothing */
        return( 1 ) ;

    return( 0 ) ;
}


/***************************************************************************
 *
 *    get_element()
 *
 *    Create a new instance of Element
 *
 ****************************************************************************/

Element *get_element(long process_id)
{
    Element *p ;

    /* Lock the free list */
    LOCK(global->free_element_lock);

    /* Test pointer */
    if( global->free_element == 0 )
        {
            printf( "Fatal: Ran out of element buffer\n" ) ;
            UNLOCK(global->free_element_lock);
            exit( 1 ) ;
        }

    /* Get an element data structure */
    p = global->free_element ;
    global->free_element = p->center ;
    global->n_free_elements-- ;

    /* Unlock the list */
    UNLOCK(global->free_element_lock);

    /* Clear pointers just in case.. */
    p->parent             = 0 ;
    p->center             = 0 ;
    p->top                = 0 ;
    p->right              = 0 ;
    p->left               = 0 ;
    p->interactions       = 0 ;
    p->n_interactions     = 0 ;
    p->vis_undef_inter    = 0 ;
    p->n_vis_undef_inter  = 0 ;

    return( p ) ;
}



/***************************************************************************
 *
 *    leaf_element()
 *
 *    Returns TRUE(1) if this element is a leaf.
 *
 *    For strong and processor memory consistency models, this routine is not
 *    necessary. For weak and release consistency models, elem->center must be
 *    tested within the Lock.
 *
 ****************************************************************************/

long leaf_element(Element *elem, long process_id)
{
    long leaf ;

    LOCK(elem->elem_lock->lock);
    leaf  = _LEAF_ELEMENT(elem) ;
    UNLOCK(elem->elem_lock->lock);

    return( leaf ) ;
}


/***************************************************************************
 *
 *    init_elemlist()
 *
 *    Initialize Element free list
 *
 ****************************************************************************/

void init_elemlist(long process_id)
{
    long i ;

    /* Initialize Element free list */
    for( i = 0 ; i < MAX_ELEMENTS-1 ; i++ )
        {
            global->element_buf[i].center = &global->element_buf[i+1] ;
            /* Initialize lock variable */
            global->element_buf[i].elem_lock
                = get_sharedlock( SHARED_LOCK_SEG1, process_id ) ;
        }
    global->element_buf[ MAX_ELEMENTS-1 ].center = 0 ;
    global->element_buf[ MAX_ELEMENTS-1 ].elem_lock
        = get_sharedlock( SHARED_LOCK_SEG1, process_id ) ;

    global->free_element = global->element_buf ;
    global->n_free_elements = MAX_ELEMENTS ;
    LOCKINIT(global->free_element_lock);
}

/***************************************************************************
 *
 *    print_element()
 *
 *    Print contents of an element.
 *
 ****************************************************************************/

void print_element(Element *elem, long process_id)
{
    printf( "Element (%ld)\n", (long)elem ) ;

    print_point( &elem->ev1->p ) ;
    print_point( &elem->ev2->p ) ;
    print_point( &elem->ev3->p ) ;

    printf( "Radiosity:" ) ;     print_rgb( &elem->rad ) ;
}






/***************************************************************************
 ****************************************************************************
 *
 *    Methods for Interaction object
 *
 ****************************************************************************
 ****************************************************************************/
/***************************************************************************
 *
 *    foreach_interaction_in_element()
 *
 *    General purpose driver. For each interaction of the element, apply
 *    specified function.  The function is passed a pointer to the interaction.
 *    Traversal is linear.
 *
 ****************************************************************************/

void foreach_interaction_in_element(Element *elem, void (*func)(), long arg1, long process_id)
{
    Interaction *inter ;

    if( elem == 0 )
        return ;

    for( inter = elem->interactions ; inter ; inter = inter->next )
        func( elem, inter, arg1, process_id ) ;
}



/***************************************************************************
 *
 *    compute_interaction()
 *    compute_formfactor()
 *
 *    compute_interaction() computes all the interaction parameters and fills
 *    in the entries of Interaction data structure.
 *    compute_formfactor() computes the formfactor only.
 *
 ****************************************************************************/

void compute_formfactor(Element *e_src, Element *e_dst, Interaction *inter, long process_id)
{
    float ff_c, ff_1, ff_2, ff_3 ;
    float ff_c1, ff_c2, ff_c3, ff_avg ;
    Vertex pc_src, pc_dst ;
    Vertex pc1_src, pc2_src, pc3_src ;
    float ff_min, ff_max, ff_err ;

    /* Estimate FF using disk approximation */
    /* (1) Compute FF(diff-disc) from the center of src to the destination */
    four_center_points( &e_src->ev1->p, &e_src->ev2->p, &e_src->ev3->p,
                       &pc_src, &pc1_src, &pc2_src, &pc3_src ) ;
    center_point( &e_dst->ev1->p, &e_dst->ev2->p, &e_dst->ev3->p, &pc_dst ) ;
    ff_c  = compute_diff_disc_formfactor( &pc_src,  e_src, &pc_dst, e_dst, process_id ) ;
    ff_c1 = compute_diff_disc_formfactor( &pc1_src, e_src, &pc_dst, e_dst, process_id ) ;
    ff_c2 = compute_diff_disc_formfactor( &pc2_src, e_src, &pc_dst, e_dst, process_id ) ;
    ff_c3 = compute_diff_disc_formfactor( &pc3_src, e_src, &pc_dst, e_dst, process_id ) ;
    if( ff_c  < 0 ) ff_c  = 0 ;
    if( ff_c1 < 0 ) ff_c1 = 0 ;
    if( ff_c2 < 0 ) ff_c2 = 0 ;
    if( ff_c3 < 0 ) ff_c3 = 0 ;
    ff_avg = (ff_c + ff_c1 + ff_c2 + ff_c3) * (float)0.25 ;
    ff_min = ff_max = ff_c ;
    if( ff_min > ff_c1 ) ff_min = ff_c1 ;
    if( ff_min > ff_c2 ) ff_min = ff_c2 ;
    if( ff_min > ff_c3 ) ff_min = ff_c3 ;
    if( ff_max < ff_c1 ) ff_max = ff_c1 ;
    if( ff_max < ff_c2 ) ff_max = ff_c2 ;
    if( ff_max < ff_c3 ) ff_max = ff_c3 ;

    /* (2) Compute FF(diff-disc) from the 3 vertices of the source */
    ff_1 = compute_diff_disc_formfactor( &e_src->ev1->p, e_src,
                                        &pc_dst, e_dst, process_id ) ;
    ff_2 = compute_diff_disc_formfactor( &e_src->ev2->p, e_src,
                                        &pc_dst, e_dst, process_id ) ;
    ff_3 = compute_diff_disc_formfactor( &e_src->ev3->p, e_src,
                                        &pc_dst, e_dst, process_id ) ;

    /* (3) Find FF min and max */
    ff_min = ff_max = ff_c ;
    if( ff_min > ff_1 ) ff_min = ff_1 ;
    if( ff_min > ff_2 ) ff_min = ff_2 ;
    if( ff_min > ff_3 ) ff_min = ff_3 ;
    if( ff_max < ff_1 ) ff_max = ff_1 ;
    if( ff_max < ff_2 ) ff_max = ff_2 ;
    if( ff_max < ff_3 ) ff_max = ff_3 ;

    /* (4) Clip FF(diff-disc) if it is negative */
    if( ff_avg < 0 )
        ff_avg = 0 ;
    inter->formfactor_out = ff_avg ;

    /* (5) Then find maximum difference from the FF at the center */
    ff_err = (ff_max - ff_avg) ;
    if( ff_err < (ff_avg - ff_min) )
        ff_err = ff_avg - ff_min ;
    inter->formfactor_err = ff_err ;

    /* (6) Correct visibility if partially visible */
    if( (ff_avg < 0) && (inter->visibility == 0) )
        /* All ray missed the visible portion of the elements.
           Set visibility to a non-zero value manually */
        /** inter->visibility = FF_VISIBILITY_ERROR **/ ;

    /* (7) Fill destination */
    inter->destination = e_dst ;
}


static float _diff_disc_formfactor(Vertex *p, Element *e_src, Vertex *p_disc, float area, Vertex *normal, long process_id)
{
    Vertex vec_sd ;
    float dist_sq ;
    float fnorm ;
    float  cos_s, cos_d, angle_factor ;

    vec_sd.x = p_disc->x - p->x ;
    vec_sd.y = p_disc->y - p->y ;
    vec_sd.z = p_disc->z - p->z ;
    dist_sq = vec_sd.x*vec_sd.x + vec_sd.y*vec_sd.y + vec_sd.z*vec_sd.z ;

    fnorm = area / ((float)M_PI * dist_sq  + area) ;

    /* (2) Now, consider angle to the other patch from the normal. */
    normalize_vector( &vec_sd, &vec_sd ) ;
    cos_s =  inner_product( &vec_sd, &e_src->patch->plane_equ.n ) ;
    cos_d = -inner_product( &vec_sd, normal ) ;
    angle_factor = cos_s * cos_d ;

    /* Return the form factor */
    return( fnorm * angle_factor ) ;
}


static float compute_diff_disc_formfactor(Vertex *p, Element *e_src, Vertex *p_disc, Element *e_dst, long process_id)
{
    Vertex p_c, p_c1, p_c2, p_c3 ;
    float quarter_area ;
    float ff_c, ff_c1, ff_c2, ff_c3 ;

    four_center_points( &e_dst->ev1->p, &e_dst->ev2->p, &e_dst->ev3->p,
                       &p_c, &p_c1, &p_c2, &p_c3 ) ;

    quarter_area = e_dst->area * (float)0.25 ;

    ff_c = _diff_disc_formfactor( p, e_src, &p_c,  quarter_area,
                                 &e_dst->patch->plane_equ.n, process_id ) ;
    ff_c1= _diff_disc_formfactor( p, e_src, &p_c1, quarter_area,
                                 &e_dst->patch->plane_equ.n, process_id ) ;
    ff_c2= _diff_disc_formfactor( p, e_src, &p_c2, quarter_area,
                                 &e_dst->patch->plane_equ.n, process_id ) ;
    ff_c3= _diff_disc_formfactor( p, e_src, &p_c3, quarter_area,
                                 &e_dst->patch->plane_equ.n, process_id ) ;

    if( ff_c  < 0 ) ff_c  = 0 ;
    if( ff_c1 < 0 ) ff_c1 = 0 ;
    if( ff_c2 < 0 ) ff_c2 = 0 ;
    if( ff_c3 < 0 ) ff_c3 = 0 ;

    return( ff_c + ff_c1 + ff_c2 + ff_c3 ) ;
}


void compute_interaction(Element *e_src, Element *e_dst, Interaction *inter, long subdiv, long process_id)
{
    /* (1) Check visibility. */
    if( NO_VISIBILITY_NECESSARY(subdiv) )
        inter->visibility = 1.0 ;
    else
        inter->visibility = VISIBILITY_UNDEF ;


    /* (2) Compute formfactor */
    compute_formfactor( e_src, e_dst, inter, process_id ) ;
}



/***************************************************************************
 *
 *    insert_interaction()
 *    delete_interaction()
 *    insert_vis_undef_interaction()
 *    delete_vis_undef_interaction()
 *
 *    Insert/Delete interaction from the interaction list.
 *
 ****************************************************************************/


void insert_interaction(Element *elem, Interaction *inter, long process_id)
{
    /* Link from patch 1 to patch 2 */
    LOCK(elem->elem_lock->lock);
    inter->next = elem->interactions ;
    elem->interactions = inter ;
    elem->n_interactions++ ;
    UNLOCK(elem->elem_lock->lock);
}



void delete_interaction(Element *elem, Interaction *prev, Interaction *inter, long process_id)
{
    /* Remove from the list */
    LOCK(elem->elem_lock->lock);
    if( prev == 0 )
        elem->interactions = inter->next ;
    else
        prev->next = inter->next ;
    elem->n_interactions-- ;
    UNLOCK(elem->elem_lock->lock);

    /* Return to the free list */
    free_interaction( inter, process_id ) ;
}



void insert_vis_undef_interaction(Element *elem, Interaction *inter, long process_id)
{
    /* Link from patch 1 to patch 2 */
    LOCK(elem->elem_lock->lock);
    inter->next = elem->vis_undef_inter ;
    elem->vis_undef_inter = inter ;
    elem->n_vis_undef_inter++ ;
    UNLOCK(elem->elem_lock->lock);
}

void delete_vis_undef_interaction(Element *elem, Interaction *prev, Interaction *inter, long process_id)
{
    /* Remove from the list */
    LOCK(elem->elem_lock->lock);
    if( prev == 0 )
        elem->vis_undef_inter = inter->next ;
    else
        prev->next = inter->next ;
    elem->n_vis_undef_inter-- ;
    UNLOCK(elem->elem_lock->lock);
}


/***************************************************************************
 *
 *    get_interaction()
 *    free_interaction()
 *
 *    Create/delete an instance of an interaction.
 *
 ****************************************************************************/

Interaction *get_interaction(long process_id)
{
    Interaction *p ;

    /* Lock the free list */
    LOCK(global->free_interaction_lock);

    /* Test pointer */
    if( global->free_interaction == 0 )
        {
            printf( "Fatal: Ran out of interaction buffer\n" ) ;
            UNLOCK(global->free_interaction_lock);
            exit( 1 ) ;
        }

    /* Get an element data structure */
    p = global->free_interaction ;
    global->free_interaction = p->next ;
    global->n_free_interactions-- ;

    /* Unlock the list */
    UNLOCK(global->free_interaction_lock);

    /* Clear pointers just in case.. */
    p->next   = 0 ;
    p->destination = 0 ;


    return( p ) ;
}



void free_interaction(Interaction *interaction, long process_id)
{
    /* Lock the free list */
    LOCK(global->free_interaction_lock);

    /* Get a task data structure */
    interaction->next = global->free_interaction ;
    global->free_interaction = interaction ;
    global->n_free_interactions++ ;

    /* Unlock the list */
    UNLOCK(global->free_interaction_lock);
}


/***************************************************************************
 *
 *    init_interactionlist()
 *
 *    Initialize Interaction free list
 *
 ****************************************************************************/

void init_interactionlist(long process_id)
{
    long i ;

    /* Initialize Interaction free list */
    for( i = 0 ; i < MAX_INTERACTIONS-1 ; i++ )
        global->interaction_buf[i].next = &global->interaction_buf[i+1] ;
    global->interaction_buf[ MAX_INTERACTIONS-1 ].next = 0 ;
    global->free_interaction = global->interaction_buf ;
    global->n_free_interactions = MAX_INTERACTIONS ;
    LOCKINIT(global->free_interaction_lock);
}


/***************************************************************************
 *
 *    print_interaction()
 *
 *    Print interaction data structure.
 *
 ****************************************************************************/

void print_interaction(Interaction *inter, long process_id)
{

    printf( "Interaction(0x%ld)\n", (long)inter ) ;
    printf( "    Dest: Elem (0x%ld) of patch %ld\n",
           (long)inter->destination, inter->destination->patch->seq_no ) ;
    printf( "    Fout: %f    Vis: %f\n",
           inter->formfactor_out,
           inter->visibility ) ;
    printf( "    Next: 0x%p\n", inter->next ) ;
}
